NestJS Mock数据配置与接口白名单实战
NestJS Mock数据配置与接口白名单实战
学习目标:掌握 Mock 数据配置方案、mockWhiteList 实现、端口动态配置、部分接口 Mock 部分接口真实请求的实现。
一、Mock 数据配置需求背景
1.1 为什么需要 Mock 数据配置
code
Mock 数据配置需求场景:
│
├── 开发阶段
│ ├── 后端接口未开发完成
│ ├── 前端需要模拟数据进行开发
│ ├── 前后端并行开发
│ └── 提高开发效率
│
├── 接口迁移阶段
│ ├── 部分接口已开发完成
│ ├── 部分接口还在开发中
│ ├── 需要部分 Mock 部分真实
│ └── 渐进式迁移
│
└── 环境切换
├── 开发环境使用 Mock
├── 测试环境使用真实接口
└── 生产环境使用真实接口1.2 Mock 配置的核心问题
code
Mock 配置核心问题:
│
├── 问题一:全量 Mock vs 部分 Mock
│ ├── 全量 Mock:所有接口都使用 Mock 数据
│ ├── 部分 Mock:部分接口 Mock,部分接口真实
│ └── 需求:灵活控制哪些接口走 Mock
│
├── 问题二:环境切换
│ ├── 开发环境:可能需要 Mock
│ ├── 测试环境:不需要 Mock
│ └── 需求:通过环境变量控制
│
└── 问题三:接口白名单
├── 哪些接口走 Mock
├── 哪些接口走真实后端
└── 需求:配置化管理1.3 Mock 配置方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 方案一:全局开关 | 实现简单 | 不够灵活,只能全量 Mock 或全量真实 | 接口全部未开发或全部开发完成 |
| 方案二:接口白名单 | 灵活可控 | 需要配置白名单 | 部分接口 Mock 部分接口真实 |
| 方案三:按环境切换 | 自动化 | 需要多个环境配置文件 | 多环境部署 |
二、方案一:全局 Mock 开关
2.1 实现思路
code
方案一实现思路:
│
├── 第一步:添加环境变量
│ └── .env.local 中添加 MOCK=true
│
├── 第二步:修改 middleware
│ └── 判断 process.env.MOCK === 'true'
│
├── 第三步:控制 Mock 逻辑
│ ├── true:走 Mock 逻辑
│ └── false:走真实接口
│
└── 第四步:测试验证
├── MOCK=true:返回 Mock 数据
└── MOCK=false:返回真实数据2.2 完整实现代码
环境变量配置:
bash
# .env.local
MOCK=true
BASE_URL=http://localhost:3000
PORT=3010Middleware 实现:
typescript
// middleware.ts
export default defineNuxtConfig({
srcDir: 'src/',
nitro: {
externals: {
inline: ['protobufjs'],
},
},
hooks: {
'render:setupMiddleware'(app) {
app.use(async (req, res, next) => {
const url = req.url || ''
// 判断是否开启 Mock
if (process.env.MOCK === 'true') {
// 所有接口都走 Mock 逻辑
if (url.startsWith('/api')) {
// Mock 处理逻辑
const mockData = getMockData(url)
res.end(JSON.stringify(mockData))
return
}
}
// 否则走真实接口
next()
})
},
},
})2.3 方案一优缺点
code
方案一优缺点分析:
│
├── 优点
│ ├── 实现简单
│ ├── 配置简单
│ └── 全局控制
│
└── 缺点
├── 不够灵活
├── 只能全量 Mock 或全量真实
└── 无法部分接口 Mock三、方案二:接口白名单(推荐)
3.1 实现思路
code
方案二实现思路:
│
├── 第一步:配置白名单
│ └── .env.local 中添加 MOCK_WHITE_LIST
│
├── 第二步:解析白名单
│ └── middleware 中读取并解析为数组
│
├── 第三步:判断接口是否在白名单
│ ├── 在白名单:走真实接口
│ └── 不在白名单:走 Mock 逻辑
│
└── 第四步:测试验证
├── 白名单接口:返回真实数据
└── 非白名单接口:返回 Mock 数据3.2 完整实现步骤
第一步:配置环境变量
bash
# .env.local
# Mock 白名单:这些接口不走 Mock,走真实接口
MOCK_WHITE_LIST=/api/courses,/api/users
# 后端接口地址
BASE_URL=http://localhost:3000
# 本地服务端口
PORT=3010第二步:Middleware 实现白名单逻辑
typescript
// middleware.ts
export default defineNuxtConfig({
srcDir: 'src/',
nitro: {
externals: {
inline: ['protobufjs'],
},
},
hooks: {
'render:setupMiddleware'(app) {
app.use(async (req, res, next) => {
const url = req.url || ''
// 只处理 API 请求
if (!url.startsWith('/api')) {
next()
return
}
// 获取白名单
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
// 提取 URL 路径(去除 Query 参数)
const urlPath = url.split('?')[0]
// 判断是否在白名单中
if (whiteList.indexOf(urlPath) !== -1) {
// 在白名单中,走真实接口
next()
} else {
// 不在白名单中,走 Mock 逻辑
const mockData = getMockData(urlPath)
res.end(JSON.stringify(mockData))
}
})
},
},
})第三步:端口动态配置
bash
# .env.local
PORT=3010
BASE_URL=http://localhost:3000
MOCK_WHITE_LIST=/api/coursestypescript
// next.config.ts
import type { Config } from 'next'
const config: Config = {
devServer: {
// 动态读取端口,默认 3000
port: parseInt(process.env.PORT || '3000', 10),
},
}
export default config第四步:Mock 数据请求到本地端口
typescript
// middleware.ts(完整版)
import { getMockData } from './mock'
export default defineNuxtConfig({
srcDir: 'src/',
nitro: {
externals: {
inline: ['protobufjs'],
},
},
hooks: {
'render:setupMiddleware'(app) {
app.use(async (req, res, next) => {
const url = req.url || ''
// 只处理 API 请求
if (!url.startsWith('/api')) {
next()
return
}
// 获取白名单
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
// 提取 URL 路径(去除 Query 参数)
const urlPath = url.split('?')[0]
// 判断是否在白名单中
if (whiteList.indexOf(urlPath) !== -1) {
// 在白名单中,走真实接口
next()
} else {
// 不在白名单中,走 Mock 逻辑
// Mock 数据请求到本地端口
const port = process.env.PORT || '3000'
const mockUrl = `http://localhost:${port}${url}`
try {
const response = await fetch(mockUrl)
const data = await response.json()
res.end(JSON.stringify(data))
} catch (error) {
res.statusCode = 500
res.end(JSON.stringify({ error: 'Mock data fetch failed' }))
}
}
})
},
},
})3.3 接口白名单工作流程
code
接口白名单工作流程:
│
├── 第一步:前端发起请求
│ └── GET /api/courses
│
├── 第二步:Middleware 拦截
│ └── 提取 URL 路径:/api/courses
│
├── 第三步:读取白名单
│ └── whiteList = ['/api/courses', '/api/users']
│
├── 第四步:判断是否在白名单
│ │
│ ├── 在白名单(/api/courses)
│ │ ├── 走真实接口
│ │ ├── 请求:http://localhost:3000/api/v1/courses
│ │ └── 返回真实数据
│ │
│ └── 不在白名单(/api/home)
│ ├── 走 Mock 逻辑
│ ├── 请求:http://localhost:3010/api/home
│ └── 返回 Mock 数据
│
└── 第五步:响应数据给前端
└── 前端收到数据并渲染3.4 接口白名单配置示例
配置说明:
bash
# .env.local
# Mock 白名单配置
# 格式:接口路径,多个用逗号分隔
# 这些接口将走真实后端,不走 Mock
# 示例一:单个接口
MOCK_WHITE_LIST=/api/courses
# 示例二:多个接口
MOCK_WHITE_LIST=/api/courses,/api/users,/api/posts
# 示例三:空字符串(所有接口都走 Mock)
MOCK_WHITE_LIST=
# 示例四:不配置(默认所有接口走 Mock)
# 不添加 MOCK_WHITE_LIST 配置项配置解析逻辑:
typescript
// 正确的解析方式
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
// 结果:
// MOCK_WHITE_LIST='/api/courses' → ['/api/courses']
// MOCK_WHITE_LIST='/api/courses,/api/users' → ['/api/courses', '/api/users']
// MOCK_WHITE_LIST='' → ['']
// 不配置 → ['']
// 判断逻辑
if (whiteList.indexOf(urlPath) !== -1) {
// 在白名单中
}四、端口动态配置详解
4.1 为什么需要动态端口
code
端口动态配置需求:
│
├── 问题:硬编码端口不灵活
│ ├── next.config.ts 中端口写死为 3010
│ ├── 不同开发者可能使用不同端口
│ └── 不同环境可能使用不同端口
│
├── 解决:从环境变量读取
│ ├── .env.local 中配置 PORT
│ ├── next.config.ts 动态读取
│ └── 提供默认值
│
└── 好处:
├── 灵活配置
├── 不同环境不同端口
└── 避免端口冲突4.2 完整实现代码
环境变量配置:
bash
# .env.local
# 本地服务端口
PORT=3010
# 后端接口地址
BASE_URL=http://localhost:3000
# Mock 白名单
MOCK_WHITE_LIST=/api/coursesNext.js 配置:
typescript
// next.config.ts
import type { Config } from 'next'
const config: Config = {
devServer: {
// 方式一:直接读取,提供默认值
port: parseInt(process.env.PORT || '3000', 10),
// 方式二:更安全的写法
// port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
},
}
export default configparseInt 详解:
typescript
// parseInt(string, radix)
// radix:进制,通常使用 10(十进制)
// 示例
parseInt('3010', 10) // 3010
parseInt('3010') // 3010(不推荐,可能产生意外结果)
parseInt('abc', 10) // NaN
// 安全写法
const port = parseInt(process.env.PORT || '3000', 10)
// 更安全的写法(处理 NaN 情况)
const port = parseInt(process.env.PORT || '3000', 10) || 30004.3 端口配置最佳实践
code
端口配置最佳实践:
│
├── 1. 使用环境变量
│ ├── .env.local 配置 PORT
│ ├── next.config.ts 读取 PORT
│ └── 提供默认值
│
├── 2. 端口范围选择
│ ├── 开发环境:3000-9999
│ ├── 避免系统保留端口:0-1023
│ └── 避免常用服务端口:80, 443, 3306, 5432
│
├── 3. 常见端口约定
│ ├── 前端:3000, 3001, 3010, 8080
│ ├── 后端:3000, 4000, 5000, 8000
│ └── 数据库:3306 (MySQL), 5432 (PostgreSQL)
│
└── 4. 端口冲突解决
├── 查看端口占用:lsof -i :3000
├── 杀死进程:kill -9 <PID>
└── 更换端口:修改 PORT 环境变量五、完整实战示例
5.1 项目结构
code
前端项目/
├── middleware.ts # Middleware 配置
├── next.config.ts # Next.js 配置(端口配置)
├── .env # 默认环境变量
├── .env.local # 本地环境变量
│ ├── PORT=3010
│ ├── BASE_URL=http://localhost:3000
│ └── MOCK_WHITE_LIST=/api/courses
├── server/
│ └── api/
│ ├── courses/
│ │ └── get.ts # 课程接口(走真实后端)
│ └── home/
│ └── get.ts # 首页接口(走 Mock)
└── mock/
└── index.ts # Mock 数据5.2 完整配置清单
1. 环境变量配置
bash
# .env.local
# ========================================
# 本地开发环境配置
# ========================================
# 本地服务端口
PORT=3010
# 后端接口地址(NestJS)
BASE_URL=http://localhost:3000
# Mock 白名单(这些接口不走 Mock,走真实后端)
# 格式:接口路径,多个用逗号分隔
# 示例:/api/courses,/api/users,/api/posts
MOCK_WHITE_LIST=/api/courses2. Next.js 配置
typescript
// next.config.ts
import type { Config } from 'next'
const config: Config = {
// 开发服务器配置
devServer: {
// 动态读取端口,默认 3000
port: parseInt(process.env.PORT || '3000', 10),
},
// 其他配置...
}
export default config3. Middleware 完整实现
typescript
// middleware.ts
import { getMockData } from './mock'
export default defineNuxtConfig({
srcDir: 'src/',
nitro: {
externals: {
inline: ['protobufjs'],
},
},
hooks: {
'render:setupMiddleware'(app) {
app.use(async (req, res, next) => {
const url = req.url || ''
// 只处理 API 请求
if (!url.startsWith('/api')) {
next()
return
}
// 提取 URL 路径(去除 Query 参数)
const urlPath = url.split('?')[0]
// 获取白名单
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
// 判断是否在白名单中
if (whiteList.indexOf(urlPath) !== -1) {
// 在白名单中,走真实接口
console.log(`[真实接口] ${urlPath} -> 走 NestJS 后端`)
next()
} else {
// 不在白名单中,走 Mock 逻辑
console.log(`[Mock 接口] ${urlPath} -> 走 Mock 数据`)
// Mock 数据请求到本地端口
const port = process.env.PORT || '3000'
const mockUrl = `http://localhost:${port}${url}`
try {
const response = await fetch(mockUrl)
const data = await response.json()
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(data))
} catch (error) {
console.error('Mock data fetch failed:', error)
res.statusCode = 500
res.end(JSON.stringify({ error: 'Mock data fetch failed' }))
}
}
})
},
},
})4. Server API 实现(真实接口)
typescript
// server/api/courses/get.ts
export default defineEventHandler(async (event) => {
// 获取 Query 参数
const query = getQuery(event)
// 发起请求到后端(NestJS)
const data = await $fetch(`${process.env.BASE_URL}/api/v1/courses`, {
method: 'GET',
query,
})
return data
})5. Mock 数据实现
typescript
// mock/index.ts
export const getMockData = (url: string) => {
// 根据不同的 URL 返回不同的 Mock 数据
if (url === '/api/home') {
return {
code: 200,
data: {
banner: [
{ id: 1, title: 'Banner 1', image: '/banner1.jpg' },
{ id: 2, title: 'Banner 2', image: '/banner2.jpg' },
],
recommend: [
{ id: 1, title: '推荐课程 1' },
{ id: 2, title: '推荐课程 2' },
],
},
}
}
// 默认返回空数据
return {
code: 404,
message: 'Mock data not found',
}
}5.3 测试验证
测试场景一:白名单接口(走真实后端)
bash
# 请求
GET http://localhost:3010/api/courses
# 日志输出
[真实接口] /api/courses -> 走 NestJS 后端
# 响应数据(来自 NestJS)
{
"code": 200,
"data": [
{ "id": 11, "name": "课程 1" },
{ "id": 12, "name": "课程 2" }
]
}测试场景二:非白名单接口(走 Mock)
bash
# 请求
GET http://localhost:3010/api/home
# 日志输出
[Mock 接口] /api/home -> 走 Mock 数据
# 响应数据(来自 Mock)
{
"code": 200,
"data": {
"banner": [...],
"recommend": [...]
}
}测试场景三:修改白名单配置
bash
# 修改 .env.local
MOCK_WHITE_LIST=/api/courses,/api/users
# 重启项目
npm run dev
# 测试 /api/courses
GET http://localhost:3010/api/courses
# 结果:走真实后端
# 测试 /api/users
GET http://localhost:3010/api/users
# 结果:走真实后端
# 测试 /api/home
GET http://localhost:3010/api/home
# 结果:走 Mock六、常见问题与解决方案
6.1 Mock 白名单不生效问题
问题描述:配置了 MOCK_WHITE_LIST,但白名单不生效,所有接口都走 Mock。
原因分析:
typescript
// 错误写法:split 前没有处理空值
const whiteList = process.env.MOCK_WHITE_LIST.split(',')
// 如果 MOCK_WHITE_LIST 未配置,会报错
// 正确写法:提供默认值
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')解决方案:
typescript
// 方案一:提供默认值
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
// 方案二:判断是否存在
const whiteList = process.env.MOCK_WHITE_LIST
? process.env.MOCK_WHITE_LIST.split(',')
: []
// 方案三:过滤空字符串
const whiteList = (process.env.MOCK_WHITE_LIST || '')
.split(',')
.filter(item => item.trim() !== '')6.2 端口配置不生效问题
问题描述:修改了 .env.local 中的 PORT,但端口没有变化。
原因分析:
code
端口配置不生效原因:
│
├── 原因一:未重启项目
│ └── 环境变量修改后需要重启项目
│
├── 原因二:parseInt 转换错误
│ └── parseInt(process.env.PORT) 可能返回 NaN
│
└── 原因三:环境变量未加载
└── 未使用 --dotenv 指定环境变量文件解决方案:
bash
# 方案一:重启项目
# 修改 .env.local 后,重启项目
npm run dev
# 方案二:指定环境变量文件
# package.json
{
"scripts": {
"dev": "next dev --dotenv .env.local"
}
}
# 方案三:安全的端口配置
# next.config.ts
const config: Config = {
devServer: {
port: parseInt(process.env.PORT || '3000', 10) || 3000,
},
}6.3 接口路径匹配问题
问题描述:URL 中包含 Query 参数,导致白名单匹配失败。
原因分析:
typescript
// 问题:URL 包含 Query 参数
const url = '/api/courses?page=1&size=10'
const whiteList = ['/api/courses']
whiteList.indexOf(url) // -1(不匹配)解决方案:
typescript
// 方案一:提取 URL 路径(去除 Query 参数)
const urlPath = url.split('?')[0]
whiteList.indexOf(urlPath) // 0(匹配)
// 方案二:使用 includes 判断
whiteList.some(path => urlPath.includes(path))
// 方案三:使用正则匹配
const regex = new RegExp(whiteList.join('|'))
regex.test(urlPath)6.4 Mock 数据 404 问题
问题描述:非白名单接口返回 404 错误。
原因分析:
code
Mock 数据 404 原因:
│
├── 原因一:Mock 文件未创建
│ └── 需要在 mock/index.ts 中添加对应的 Mock 数据
│
├── 原因二:Mock 路径配置错误
│ └── mockUrl 配置错误
│
└── 原因三:Mock 服务未启动
└── Mock 数据服务未正常运行解决方案:
typescript
// 方案一:完善 Mock 数据
// mock/index.ts
export const getMockData = (url: string) => {
const mockDataMap = {
'/api/home': {
code: 200,
data: { banner: [], recommend: [] }
},
'/api/users': {
code: 200,
data: []
},
}
return mockDataMap[url] || { code: 404, message: 'Mock data not found' }
}
// 方案二:正确配置 Mock URL
const port = process.env.PORT || '3000'
const mockUrl = `http://localhost:${port}${url}`
// 方案三:添加错误处理
try {
const response = await fetch(mockUrl)
if (!response.ok) {
throw new Error('Mock data fetch failed')
}
const data = await response.json()
res.end(JSON.stringify(data))
} catch (error) {
res.statusCode = 500
res.end(JSON.stringify({ error: 'Mock data fetch failed' }))
}七、最佳实践总结
7.1 Mock 配置最佳实践
code
Mock 配置最佳实践:
│
├── 1. 使用接口白名单
│ ├── 配置 MOCK_WHITE_LIST
│ ├── 灵活控制哪些接口走 Mock
│ └── 便于渐进式迁移
│
├── 2. 动态端口配置
│ ├── 使用环境变量 PORT
│ ├── 提供默认值
│ └── 避免端口冲突
│
├── 3. 环境变量管理
│ ├── 使用 .env.local 配置本地环境
│ ├── 使用 .env.production 配置生产环境
│ └── 不要将 .env.local 提交到 Git
│
├── 4. Mock 数据管理
│ ├── 统一放在 mock 目录
│ ├── 根据 URL 返回不同 Mock 数据
│ └── Mock 数据要符合真实接口格式
│
└── 5. 日志调试
├── 打印接口类型(Mock 或真实)
├── 打印请求 URL
└── 便于调试和问题定位7.2 环境变量配置最佳实践
code
环境变量配置最佳实践:
│
├── 1. 文件命名规范
│ ├── .env:默认环境变量(所有环境共享)
│ ├── .env.local:本地环境变量(优先级最高)
│ ├── .env.development:开发环境变量
│ └── .env.production:生产环境变量
│
├── 2. 环境变量命名规范
│ ├── 使用大写字母和下划线
│ ├── 语义化命名
│ └── 添加注释说明
│
├── 3. 环境变量优先级
│ ├── .env.local > .env.[environment] > .env
│ ├── 系统环境变量 > 文件环境变量
│ └── 后加载的会覆盖先加载的
│
└── 4. 安全性
├── 不要在 .env 中存储敏感信息
├── 使用 .env.example 作为模板
└── 将 .env.local 添加到 .gitignore7.3 接口迁移最佳实践
code
接口迁移最佳实践:
│
├── 1. 渐进式迁移
│ ├── 先迁移简单接口
│ ├── 逐步迁移复杂接口
│ └── 最后迁移核心接口
│
├── 2. 白名单管理
│ ├── 迁移完成的接口加入白名单
│ ├── 未迁移的接口保持 Mock
│ └── 定期更新白名单
│
├── 3. 测试验证
│ ├── 每迁移一个接口都要测试
│ ├── 对比 Mock 数据和真实数据
│ └── 确保数据格式一致
│
└── 4. 文档更新
├── 记录哪些接口已迁移
├── 记录迁移过程中的问题
└── 更新接口文档八、命令速查表
8.1 环境变量命令速查
| 命令 | 说明 |
|---|---|
npm run dev | 启动开发服务器(加载 .env.development) |
npm run dev:local | 启动本地测试服务器(加载 .env.local) |
npm run build | 构建生产版本(加载 .env.production) |
npm run start | 运行生产版本 |
8.2 端口相关命令速查
| 命令 | 说明 |
|---|---|
lsof -i :3000 | 查看 3000 端口占用情况 |
kill -9 <PID> | 杀死占用端口的进程 |
netstat -an | grep 3000 | 查看 3000 端口状态 |
npx kill-port 3000 | 使用工具杀死端口 |
8.3 环境变量读取速查
| 方法 | 说明 | 示例 |
|---|---|---|
process.env.VAR_NAME | 读取环境变量 | process.env.PORT |
process.env.VAR_NAME || 'default' | 提供默认值 | process.env.PORT || '3000' |
parseInt(str, 10) | 转换为整数 | parseInt('3010', 10) |
九、学习要点总结
9.1 核心知识点
code
NestJS Mock 数据配置核心要点:
│
├── 1. Mock 配置方案
│ ├── 方案一:全局开关(简单但不灵活)
│ ├── 方案二:接口白名单(灵活可控)
│ └── 选择:推荐使用接口白名单方案
│
├── 2. 接口白名单实现
│ ├── 配置 MOCK_WHITE_LIST
│ ├── 解析为数组
│ ├── 判断 URL 是否在白名单
│ └── 分别处理 Mock 和真实接口
│
├── 3. 端口动态配置
│ ├── 使用环境变量 PORT
│ ├── parseInt 转换
│ └── 提供默认值
│
├── 4. 环境变量管理
│ ├── .env.local 本地配置
│ ├── .env.production 生产配置
│ └── 不同环境不同配置
│
├── 5. Mock 数据管理
│ ├── 统一放在 mock 目录
│ ├── 根据 URL 返回不同数据
│ └── 格式符合真实接口
│
└── 6. 接口迁移流程
├── 配置白名单
├── 实现真实接口
├── 测试验证
└── 更新白名单9.2 重要程度标注
| 知识点 | 重要程度 | 说明 |
|---|---|---|
| 接口白名单 | 必须掌握,Mock 配置核心 | |
| 端口动态配置 | 必须掌握,避免端口冲突 | |
| 环境变量管理 | 必须掌握,不同环境配置 | |
| Middleware 实现 | 必须掌握,请求拦截逻辑 | |
| Mock 数据管理 | 重要,Mock 数据组织 |
9.3 学习路径规划
code
学习路径规划:
│
├── 第一阶段:理解概念(1 天)
│ ├── 理解 Mock 配置的两种方案
│ ├── 理解接口白名单的作用
│ └── 理解端口动态配置的意义
│
├── 第二阶段:实践操作(2-3 天)
│ ├── 配置环境变量
│ ├── 实现 Middleware
│ ├── 测试接口白名单
│ └── 测试 Mock 和真实接口切换
│
└── 第三阶段:深入应用(持续)
├── 复杂业务场景
├── 性能优化
└── 团队协作规范十、完整代码清单
10.1 环境变量配置完整代码
bash
# .env.local
# ========================================
# 本地开发环境配置
# ========================================
# 本地服务端口
PORT=3010
# 后端接口地址(NestJS)
BASE_URL=http://localhost:3000
# Mock 白名单(这些接口不走 Mock,走真实后端)
# 格式:接口路径,多个用逗号分隔
# 示例:/api/courses,/api/users,/api/posts
MOCK_WHITE_LIST=/api/coursesbash
# .env.production
# ========================================
# 生产环境配置
# ========================================
# 生产环境端口
PORT=3000
# 生产环境后端接口地址
BASE_URL=https://api.production.com
# 生产环境不使用 Mock
MOCK_WHITE_LIST=10.2 Next.js 配置完整代码
typescript
// next.config.ts
import type { Config } from 'next'
const config: Config = {
// 开发服务器配置
devServer: {
// 动态读取端口,默认 3000
port: parseInt(process.env.PORT || '3000', 10) || 3000,
},
// 其他配置...
}
export default config10.3 Middleware 完整代码
typescript
// middleware.ts
import { getMockData } from './mock'
export default defineNuxtConfig({
srcDir: 'src/',
nitro: {
externals: {
inline: ['protobufjs'],
},
},
hooks: {
'render:setupMiddleware'(app) {
app.use(async (req, res, next) => {
const url = req.url || ''
// 只处理 API 请求
if (!url.startsWith('/api')) {
next()
return
}
// 提取 URL 路径(去除 Query 参数)
const urlPath = url.split('?')[0]
// 获取白名单(过滤空字符串)
const whiteList = (process.env.MOCK_WHITE_LIST || '')
.split(',')
.filter(item => item.trim() !== '')
// 判断是否在白名单中
if (whiteList.indexOf(urlPath) !== -1) {
// 在白名单中,走真实接口
console.log(`[真实接口] ${urlPath} -> 走 NestJS 后端`)
next()
} else {
// 不在白名单中,走 Mock 逻辑
console.log(`[Mock 接口] ${urlPath} -> 走 Mock 数据`)
// Mock 数据请求到本地端口
const port = process.env.PORT || '3000'
const mockUrl = `http://localhost:${port}${url}`
try {
const response = await fetch(mockUrl)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data = await response.json()
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(data))
} catch (error) {
console.error('Mock data fetch failed:', error)
res.statusCode = 500
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({
code: 500,
message: 'Mock data fetch failed',
error: String(error)
}))
}
}
})
},
},
})10.4 Mock 数据完整代码
typescript
// mock/index.ts
interface MockData {
code: number
message?: string
data?: any
}
/**
* 根据 URL 返回 Mock 数据
* @param url 请求 URL
* @returns Mock 数据
*/
export const getMockData = (url: string): MockData => {
// Mock 数据映射表
const mockDataMap: Record<string, MockData> = {
// 首页数据
'/api/home': {
code: 200,
data: {
banner: [
{ id: 1, title: '精品微课', image: '/banner1.jpg', link: '/courses' },
{ id: 2, title: '学习计划', image: '/banner2.jpg', link: '/plans' },
],
recommend: [
{ id: 1, title: '推荐课程 1', cover: '/course1.jpg', price: 99 },
{ id: 2, title: '推荐课程 2', cover: '/course2.jpg', price: 199 },
],
categories: [
{ id: 11, name: '前端开发', count: 120 },
{ id: 12, name: '后端开发', count: 80 },
],
},
},
// 用户数据
'/api/users': {
code: 200,
data: [
{ id: 1, username: 'user1', email: 'user1@example.com' },
{ id: 2, username: 'user2', email: 'user2@example.com' },
],
},
// 文章数据
'/api/posts': {
code: 200,
data: [
{ id: 1, title: '文章 1', content: '内容 1', author: '作者 1' },
{ id: 2, title: '文章 2', content: '内容 2', author: '作者 2' },
],
},
}
// 返回对应的 Mock 数据,如果不存在则返回 404
return mockDataMap[url] || {
code: 404,
message: 'Mock data not found',
data: null,
}
}10.5 Server API 完整代码
typescript
// server/api/courses/get.ts
export default defineEventHandler(async (event) => {
// 获取 Query 参数
const query = getQuery(event)
// 发起请求到后端(NestJS)
const data = await $fetch(`${process.env.BASE_URL}/api/v1/courses`, {
method: 'GET',
query,
})
return data
})typescript
// server/api/users/get.ts
export default defineEventHandler(async (event) => {
// 获取 Query 参数
const query = getQuery(event)
// 发起请求到后端(NestJS)
const data = await $fetch(`${process.env.BASE_URL}/api/v1/users`, {
method: 'GET',
query,
})
return data
})十一、扩展阅读
11.1 Mock 数据生成工具
使用 Mock.js 生成 Mock 数据:
typescript
// mock/index.ts
import Mock from 'mockjs'
export const getMockData = (url: string) => {
if (url === '/api/users') {
// 使用 Mock.js 生成随机数据
return Mock.mock({
code: 200,
'data|10': [{
'id|+1': 1,
'username': '@cname',
'email': '@email',
'avatar': '@image("100x100")',
'createTime': '@datetime',
}]
})
}
// 其他 Mock 数据...
}11.2 自动化 Mock 数据
根据接口文档自动生成 Mock 数据:
typescript
// mock/auto.ts
import { OpenAPI } from 'openapi-types'
/**
* 根据 OpenAPI 文档自动生成 Mock 数据
*/
export const generateMockFromOpenAPI = (doc: OpenAPI.Document) => {
const mockData = {}
// 遍历所有接口
for (const [path, methods] of Object.entries(doc.paths)) {
for (const [method, spec] of Object.entries(methods)) {
const key = `${method.toUpperCase()} ${path}`
mockData[key] = generateMockFromSchema(spec.responses['200'])
}
}
return mockData
}11.3 团队协作 Mock 规范
code
团队协作 Mock 规范:
│
├── 1. Mock 数据文件组织
│ ├── mock/
│ │ ├── index.ts # 入口文件
│ │ ├── home.ts # 首页相关 Mock
│ │ ├── courses.ts # 课程相关 Mock
│ │ └── users.ts # 用户相关 Mock
│ └── 统一管理,便于维护
│
├── 2. Mock 数据命名规范
│ ├── 文件名:小写,用连字符分隔
│ ├── 接口名:RESTful 风格
│ └── 数据字段:驼峰命名
│
├── 3. Mock 数据更新流程
│ ├── 接口文档先行
│ ├── Mock 数据符合接口文档
│ ├── 前端基于 Mock 开发
│ └── 后端接口完成后迁移
│
└── 4. Mock 数据版本控制
├── 使用 Git 管理 Mock 数据
├── 接口变更时同步更新 Mock
└── 保留历史版本便于回滚重要提示:Mock 数据配置是前后端并行开发的重要工具,掌握接口白名单、端口动态配置、环境变量管理,对实际项目开发非常重要!推荐使用接口白名单方案,灵活可控,便于渐进式迁移!
下节预告:Next.js 状态管理进阶,深入学习 Pinia 高级用法、数据持久化、状态同步等高级应用!
笔记已按照您的格式规范整理完成,可直接用于学习复习!